package com.github.florent37.harmonyslidr;

import ohos.agp.components.*;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.*;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Slidr extends StackLayout {
    private static final float DISTANCE_TEXT_BAR = 10;
    private static final float BUBBLE_PADDING_HORIZONTAL = 15;
    private static final float BUBBLE_PADDING_VERTICAL = 10;

    private static final float BUBBLE_ARROW_HEIGHT = 10;
    private static final float BUBBLE_ARROW_WIDTH = 20;
    private boolean moving = false;
    private Listener listener;
    private BubbleClickedListener bubbleClickedListener;
    private Settings settings;
    private float max = 1000;
    private float min = 0;
    private float currentValue = 300;
    private float oldValue = Float.MIN_VALUE;
    private List<Step> steps = new ArrayList<>();
    private float barY;
    private float barWidth;
    private float indicatorX;
    private int indicatorRadius;
    private float barCenterY;
    private Bubble bubble = new Bubble();
    private TextFormatter textFormatter = new EurosTextFormatter();
    private RegionTextFormatter regionTextFormatter = null;

    private String textMax = "";
    private String textMin = "";
    private int calculatedHieght = 0;
    private boolean isEditing = false;
    private String textEditing = "";
    private TextField editText;
    private EditListener editListener;

    public Slidr(Context context) {
        this(context, null);
    }

    public Slidr(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public Slidr(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);

        init(attrSet);
    }

    private void init(AttrSet attrSet) {
        this.settings = new Settings(this);
        this.settings.init(getContext(), attrSet);
        addDrawTask(
                new DrawTask() {
                    @Override
                    public void onDraw(Component component, Canvas canvas) {
                        updateValues();
                        ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
                        layoutConfig.height = calculatedHieght;
                        layoutConfig.width = getWidth();
                        setLayoutConfig(layoutConfig);

                        onDrawLayout(canvas);
                    }
                });

        setTouchEventListener((component, touchEvent) -> handleTouch(touchEvent));

        setLayoutRefreshedListener(component -> updateValues());
    }

    private boolean handleTouch(TouchEvent event) {
        if (isEditing) {
            return false;
        }
        final int action = event.getAction();
        switch (action) {
            case TouchEvent.PRIMARY_POINT_UP:
            case TouchEvent.CANCEL:
                actionUp();
                moving = false;
                break;
            case TouchEvent.PRIMARY_POINT_DOWN:
                final float evY = getTouchY(event, 0);

                if (evY <= barY || evY >= (barY + barWidth)) {
                    return true;
                } else {
                    moving = true;
                }
                break;

            case TouchEvent.POINT_MOVE:

                if (moving) {
                    float evX = getTouchX(event, 0);

                    evX = evX - settings.paddingCorners;
                    if (evX < 0) {
                        evX = 0;
                    }
                    if (evX > barWidth) {
                        evX = barWidth;
                    }
                    this.indicatorX = evX;

                    update();
                }
                break;
            default:
                break;
        }

        return true;
    }

    private float getTouchX(TouchEvent touchEvent, int index) {
        float x = 0;
        if (touchEvent.getPointerCount() > index) {
            int[] xy = getLocationOnScreen();
            if (xy != null && xy.length == 2) {
                x = touchEvent.getPointerScreenPosition(index).getX() - xy[0];
            } else {
                x = touchEvent.getPointerPosition(index).getX();
            }
        }
        return x;
    }

    private float getTouchY(TouchEvent touchEvent, int index) {
        float y = 0;
        if (touchEvent.getPointerCount() > index) {
            int[] xy = getLocationOnScreen();
            if (xy != null && xy.length == 2) {
                y = touchEvent.getPointerScreenPosition(index).getY() - xy[1];
            } else {
                y = touchEvent.getPointerPosition(index).getY();
            }
        }
        return y;
    }

    void actionUp() {}

    private void onDrawLayout(Canvas canvas) {
        canvas.save();
        {
            final float paddingLeft = settings.paddingCorners;
            final float paddingRight = settings.paddingCorners;

            if (isRegions()) {
                if (steps.isEmpty()) {
                    settings.paintIndicator.setColor(settings.regionColorLeft);
                    settings.paintBubble.setColor(settings.regionColorLeft);
                } else {
                    settings.paintIndicator.setColor(settings.regionColorRight);
                    settings.paintBubble.setColor(settings.regionColorRight);
                }
            } else {
                final Step stepBeforeCustor = findStepOfCustor();
                if (stepBeforeCustor != null) {
                    settings.paintIndicator.setColor(stepBeforeCustor.colorBefore);
                    settings.paintBubble.setColor(stepBeforeCustor.colorBefore);
                } else {
                    if (settings.step_colorizeAfterLast) {
                        final Step beforeCustor = findStepBeforeCustor();
                        if (beforeCustor != null) {
                            settings.paintIndicator.setColor(beforeCustor.colorAfter);
                            settings.paintBubble.setColor(beforeCustor.colorAfter);
                        }
                    } else {
                        settings.paintIndicator.setColor(settings.colorBackground);
                        settings.paintBubble.setColor(settings.colorBackground);
                    }
                }
            }

            final float radiusCorner = settings.barHeight / 2f;

            final float indicatorCenterX = indicatorX + paddingLeft;

            { // background
                final float centerCircleLeft = paddingLeft;
                final float centerCircleRight = getWidth() - paddingRight;

                // grey background
                if (isRegions()) {
                    if (steps.isEmpty()) {
                        settings.paintBar.setColor(settings.colorBackground);
                    } else {
                        settings.paintBar.setColor(settings.regionColorRight);
                    }
                } else {
                    settings.paintBar.setColor(settings.colorBackground);
                }
                canvas.drawCircle(centerCircleLeft, barCenterY, radiusCorner, settings.paintBar);
                canvas.drawCircle(centerCircleRight, barCenterY, radiusCorner, settings.paintBar);
                canvas.drawRect(
                        new RectFloat(centerCircleLeft, barY, centerCircleRight, barY + settings.barHeight),
                        settings.paintBar);

                if (isRegions()) {
                    settings.paintBar.setColor(settings.regionColorLeft);

                    canvas.drawCircle(centerCircleLeft, barCenterY, radiusCorner, settings.paintBar);
                    canvas.drawRect(
                            new RectFloat(centerCircleLeft, barY, indicatorCenterX, barY + settings.barHeight),
                            settings.paintBar);
                } else {
                    float lastX = centerCircleLeft;
                    boolean first = true;
                    for (Step step : steps) {
                        settings.paintBar.setColor(step.colorBefore);
                        if (first) {
                            canvas.drawCircle(centerCircleLeft, barCenterY, radiusCorner, settings.paintBar);
                        }

                        final float x = step.xStart + paddingLeft;
                        if (!settings.step_colorizeOnlyBeforeIndicator) {
                            canvas.drawRect(
                                    new RectFloat(lastX, barY, x, barY + settings.barHeight), settings.paintBar);
                        } else {
                            canvas.drawRect(
                                    new RectFloat(
                                            lastX, barY, Math.min(x, indicatorCenterX), barY + settings.barHeight),
                                    settings.paintBar);
                        }
                        lastX = x;

                        first = false;
                    }

                    if (settings.step_colorizeAfterLast) {
                        // find the step just below currentValue
                        for (int i = steps.size() - 1; i >= 0; i--) {
                            final Step step = steps.get(i);
                            if ((currentValue - min) > step.value) {
                                settings.paintBar.setColor(step.colorAfter);
                                canvas.drawRect(
                                        new RectFloat(
                                                step.xStart + paddingLeft,
                                                barY,
                                                indicatorCenterX,
                                                barY + settings.barHeight),
                                        settings.paintBar);
                                break;
                            }
                        }
                    }
                }
            }

            { // texts top (values)
                if (settings.drawTextOnTop) {
                    final float textY = barY;
                    if (isRegions()) {
                        float leftValue;
                        float rightValue;

                        if (settings.regions_centerText) {
                            leftValue = currentValue;
                            rightValue = max - leftValue;
                        } else {
                            leftValue = min;
                            rightValue = max;
                        }

                        if (settings.regions_textFollowRegionColor) {
                            settings.paintTextTop.setColor(settings.regionColorLeft);
                        }

                        float textX;
                        if (settings.regions_centerText) {
                            textX = (indicatorCenterX - paddingLeft) / 2f + paddingLeft;
                        } else {
                            textX = paddingLeft;
                        }

                        drawIndicatorsTextAbove(
                                canvas,
                                formatRegionValue(0, leftValue),
                                settings.paintTextTop,
                                textX,
                                textY,
                                TextAlignment.CENTER);

                        if (settings.regions_textFollowRegionColor) {
                            settings.paintTextTop.setColor(settings.regionColorRight);
                        }

                        if (settings.regions_centerText) {
                            textX = indicatorCenterX + (barWidth - indicatorCenterX - paddingLeft) / 2f + paddingLeft;
                        } else {
                            textX = paddingLeft + barWidth;
                        }
                        drawIndicatorsTextAbove(
                                canvas,
                                formatRegionValue(1, rightValue),
                                settings.paintTextTop,
                                textX,
                                textY,
                                TextAlignment.CENTER);
                    } else {
                        drawIndicatorsTextAbove(
                                canvas,
                                formatValue(min),
                                settings.paintTextTop,
                                0 + paddingLeft,
                                textY,
                                TextAlignment.CENTER);
                        for (Step step : steps) {
                            drawIndicatorsTextAbove(
                                    canvas,
                                    formatValue(step.value),
                                    settings.paintTextTop,
                                    step.xStart + paddingLeft,
                                    textY,
                                    TextAlignment.CENTER);
                        }
                        drawIndicatorsTextAbove(
                                canvas,
                                formatValue(max),
                                settings.paintTextTop,
                                getWidth(),
                                textY,
                                TextAlignment.CENTER);
                    }
                }
            }

            { // steps + bottom text
                final float bottomTextY = barY + settings.barHeight + 55;

                for (Step step : steps) {
                    if (settings.step_drawLines) {
                        canvas.drawLine(
                                new Point(step.xStart + paddingLeft, barY - settings.barHeight / 4f),
                                new Point(
                                        step.xStart + paddingLeft, barY + settings.barHeight + settings.barHeight / 4f),
                                settings.paintStep);
                    }

                    if (settings.drawTextOnBottom) {
                        drawMultilineText(
                                canvas,
                                step.name,
                                step.xStart + paddingLeft,
                                bottomTextY,
                                settings.paintTextBottom,
                                TextAlignment.CENTER);
                    }
                }

                if (settings.drawTextOnBottom) {
                    if (!textMax.isEmpty()) {
                        drawMultilineText(
                                canvas,
                                textMax,
                                getWidth(),
                                bottomTextY,
                                settings.paintTextBottom,
                                TextAlignment.CENTER);
                    }

                    if (!textMin.isEmpty()) {
                        drawMultilineText(
                                canvas, textMin, 0, bottomTextY, settings.paintTextBottom, TextAlignment.CENTER);
                    }
                }
            }

            // indicator
            {
                final Color color = settings.paintIndicator.color;
                canvas.drawCircle(indicatorCenterX, this.barCenterY, indicatorRadius, settings.paintIndicator);
                settings.paintIndicator.setColor(Color.WHITE);
                canvas.drawCircle(indicatorCenterX, this.barCenterY, indicatorRadius * 0.85f, settings.paintIndicator);
                settings.paintIndicator.setColor(color);
            }

            // bubble
            {
                if (settings.drawBubble) {
                    float bubbleCenterX = indicatorCenterX;
                    float trangleCenterX;

                    bubble.x = bubbleCenterX - bubble.width / 2f;
                    bubble.y = 0;

                    if (bubbleCenterX > getWidth() - bubble.width / 2f) {
                        bubbleCenterX = getWidth() - bubble.width / 2f;
                    } else if (bubbleCenterX - bubble.width / 2f < 0) {
                        bubbleCenterX = bubble.width / 2f;
                    }

                    trangleCenterX = (bubbleCenterX + indicatorCenterX) / 2f;

                    drawBubble(canvas, bubbleCenterX, trangleCenterX, 0);
                }
            }
        }

        canvas.restore();
    }

    private void drawText(Canvas canvas, String text, float x, float y, Paint paint, int aligment) {
        canvas.save();
        {
            canvas.translate(x, y);

            canvas.drawText(paint, text, 0, 0);
        }
        canvas.restore();
    }

    private void drawMultilineText(Canvas canvas, String text, float x, float y, Paint paint, int aligment) {
        final float lineHeight = paint.getTextSize();
        float lineY = y;
        for (CharSequence line : text.split("\n")) {
            canvas.save();
            {
                final float lineWidth = (int) paint.measureText(line.toString());
                float lineX = x;
                if (aligment == TextAlignment.CENTER) {
                    lineX -= lineWidth / 2f;
                }
                if (lineX < 0) {
                    lineX = 0;
                }

                final float right = lineX + lineWidth;
                if (right > getWidth()) {
                    lineX = getWidth() - lineWidth - settings.paddingCorners;
                }

                canvas.translate(lineX, lineY);

                canvas.drawText(paint, line.toString(), 0, 0);

                lineY += lineHeight;
            }
            canvas.restore();
        }
    }

    private void drawIndicatorsTextAbove(Canvas canvas, String text, Paint paintText, float x, float y, int alignment) {
        final float textHeight = calculateTextMultilineHeight(text, paintText);
        y -= textHeight;

        final int width = (int) paintText.measureText(text);
        if (x >= getWidth() - settings.paddingCorners) {
            x = (getWidth() - width - settings.paddingCorners / 2f);
        } else if (x <= 0) {
            x = width / 2f;
        } else {
            x = (x - width / 2f);
        }

        if (x < 0) {
            x = 0;
        }

        if (x + width > getWidth()) {
            x = getWidth() - width;
        }

        drawText(canvas, text, x, y, paintText, alignment);
    }

    private void drawBubblePath(Canvas canvas, float triangleCenterX, float height, float width) {
        final Path path = new Path();

        int padding = 3;
        final Rect rect =
                new Rect(
                        padding,
                        padding,
                        (int) width - padding,
                        (int) (height - dpToPx(BUBBLE_ARROW_HEIGHT)) - padding);

        final float roundRectHeight = (height - dpToPx(BUBBLE_ARROW_HEIGHT)) / 2;

        path.moveTo(rect.left + roundRectHeight, rect.top);
        path.lineTo(rect.right - roundRectHeight, rect.top);
        path.quadTo(rect.right, rect.top, rect.right, rect.top + roundRectHeight);
        path.lineTo(rect.right, rect.bottom - roundRectHeight);
        path.quadTo(rect.right, rect.bottom, rect.right - roundRectHeight, rect.bottom);

        path.lineTo(triangleCenterX + dpToPx(BUBBLE_ARROW_WIDTH) / 2f, height - dpToPx(BUBBLE_ARROW_HEIGHT) - padding);
        path.lineTo(triangleCenterX, height - padding);
        path.lineTo(triangleCenterX - dpToPx(BUBBLE_ARROW_WIDTH) / 2f, height - dpToPx(BUBBLE_ARROW_HEIGHT) - padding);

        path.lineTo(rect.left + roundRectHeight, rect.bottom);
        path.quadTo(rect.left, rect.bottom, rect.left, rect.bottom - roundRectHeight);
        path.lineTo(rect.left, rect.top + roundRectHeight);
        path.quadTo(rect.left, rect.top, rect.left + roundRectHeight, rect.top);
        path.close();

        canvas.drawPath(path, settings.paintBubble);
    }

    private void drawBubble(Canvas canvas, float centerX, float triangleCenterX, float y) {
        final float width = this.bubble.width;
        final float height = this.bubble.height;

        canvas.save();
        {
            canvas.translate(centerX - width / 2f, y);
            triangleCenterX -= (centerX - width / 2f);

            if (!isEditing) {
                drawBubblePath(canvas, triangleCenterX, height, width);
            } else {
                final int savedColor = settings.paintBubble.getColor().getValue();

                settings.paintBubble.setColor(settings.bubbleColorEditing);
                settings.paintBubble.setStyle(Paint.Style.FILL_STYLE);
                drawBubblePath(canvas, triangleCenterX, height, width);

                settings.paintBubble.setStyle(Paint.Style.STROKE_STYLE);
                settings.paintBubble.setColor(settings.paintIndicator.getColor());
                drawBubblePath(canvas, triangleCenterX, height, width);

                settings.paintBubble.setStyle(Paint.Style.FILL_STYLE);
                settings.paintBubble.setColor(new Color(savedColor));
            }

            if (!isEditing) {
                final String bubbleText = formatValue(getCurrentValue());
                drawText(
                        canvas,
                        bubbleText,
                        dpToPx(BUBBLE_PADDING_HORIZONTAL),
                        dpToPx(BUBBLE_PADDING_VERTICAL * 2.3F),
                        settings.paintBubbleTextCurrent,
                        TextAlignment.LEFT);
            }
        }

        canvas.restore();
    }

    private boolean isRegions() {
        return settings.modeRegion || steps.isEmpty();
    }

    private float calculateTextMultilineHeight(String text, Paint textPaint) {
        return text.split("\n").length * textPaint.getTextSize();
    }

    private float calculateBubbleTextWidth() {
        String bubbleText = formatValue(getCurrentValue());
        if (isEditing) {
            bubbleText = textEditing;
        }
        return settings.paintBubbleTextCurrent.measureText(bubbleText);
    }

    private String formatValue(float value) {
        return textFormatter.format(value);
    }

    private String formatRegionValue(int region, float value) {
        if (regionTextFormatter != null) {
            return regionTextFormatter.format(region, value);
        } else {
            return formatValue(value);
        }
    }

    public void setMax(float max) {
        this.max = max;
        updateValues();
        update();
    }

    public void setMin(float min) {
        this.min = min;
        updateValues();
        update();
    }

    public void addStep(List<Step> steps) {
        this.steps.addAll(steps);
        Collections.sort(steps);
        update();
    }

    public void addStep(Step step) {
        this.steps.add(step);
        Collections.sort(steps);
        update();
    }

    public void clearSteps() {
        this.steps.clear();
        update();
    }

    private void editBubbleEditPosition() {
    }

    public void update() {
        if (barWidth > 0f) {
            float currentPercent = indicatorX / barWidth;
            currentValue = currentPercent * (max - min) + min;
            currentValue = Math.round(currentValue);

            if (listener != null && oldValue != currentValue) {
                oldValue = currentValue;
                listener.valueChanged(Slidr.this, currentValue);
            }

            updateBubbleWidth();
            editBubbleEditPosition();
        }
        postInvalidate();
    }

    public float getCurrentValue() {
        return currentValue;
    }

    public void setCurrentValue(float value) {
        this.currentValue = value;
        updateValues();
        update();
    }

    private void setCurrentValueNoUpdate(float value) {
        this.currentValue = value;
        listener.valueChanged(Slidr.this, currentValue);
        updateValues();
    }

    private void updateBubbleWidth() {
        this.bubble.width = calculateBubbleTextWidth() + dpToPx(BUBBLE_PADDING_HORIZONTAL) * 2f;
        this.bubble.width = Math.max(150, this.bubble.width);
    }

    private Step findStepBeforeCustor() {
        for (int i = steps.size() - 1; i >= 0; i--) {
            final Step step = steps.get(i);
            if ((currentValue - min) >= step.value) {
                return step;
            }
            break;
        }
        return null;
    }

    private Step findStepOfCustor() {
        for (int i = 0; i < steps.size(); ++i) {
            final Step step = steps.get(i);
            if ((currentValue - min) <= step.value) {
                return step;
            }
        }
        return null;
    }

    public void setTextMax(String textMax) {
        this.textMax = textMax;
        postInvalidate();
    }

    public void setTextMin(String textMin) {
        this.textMin = textMin;
        postInvalidate();
    }

    private void updateValues() {
        if (currentValue < min) {
            currentValue = min;
        }

        settings.paddingCorners = settings.barHeight;

        barWidth = getWidth() - this.settings.paddingCorners * 2;

        if (settings.drawBubble) {
            updateBubbleWidth();
            this.bubble.height =
                    dpToPx(settings.textSizeBubbleCurrent)
                            + dpToPx(BUBBLE_PADDING_VERTICAL) * 2f
                            + dpToPx(BUBBLE_ARROW_HEIGHT);
        } else {
            this.bubble.height = 10;
        }

        this.barY = 0;
        if (settings.drawTextOnTop) {
            barY += DISTANCE_TEXT_BAR * 2;
            if (isRegions()) {
                float topTextHeight = 0;
                final String tmpTextLeft = formatRegionValue(0, 0);
                final String tmpTextRight = formatRegionValue(1, 0);
                topTextHeight =
                        Math.max(topTextHeight, calculateTextMultilineHeight(tmpTextLeft, settings.paintTextTop));
                topTextHeight =
                        Math.max(topTextHeight, calculateTextMultilineHeight(tmpTextRight, settings.paintTextTop));

                this.barY += topTextHeight + 3;
            } else {
                float topTextHeight = 0;

                for (Step step : steps) {
                    topTextHeight =
                            Math.max(
                                    topTextHeight,
                                    calculateTextMultilineHeight(formatValue(step.value), settings.paintTextBottom));
                }
                this.barY += topTextHeight;
            }
        } else {
            if (settings.drawBubble) {
                this.barY -= dpToPx(BUBBLE_ARROW_HEIGHT) / 1.5f;
            }
        }

        this.barY += bubble.height;

        this.barCenterY = barY + settings.barHeight / 2f;

        if (settings.indicatorInside) {
            this.indicatorRadius = (int) (settings.barHeight * .5f);
        } else {
            this.indicatorRadius = (int) (settings.barHeight * .9f);
        }

        for (Step step : steps) {
            final float stoppoverPercent = step.value / (max - min);
            step.xStart = stoppoverPercent * barWidth;
        }

        indicatorX = (currentValue - min) / (max - min) * barWidth;

        calculatedHieght = (int) (barCenterY + indicatorRadius);

        float bottomTextHeight = 0;
        if (!textMax.isEmpty()) {
            bottomTextHeight =
                    Math.max(
                            calculateTextMultilineHeight(textMax, settings.paintTextBottom),
                            calculateTextMultilineHeight(textMin, settings.paintTextBottom));
        }
        for (Step step : steps) {
            bottomTextHeight =
                    Math.max(bottomTextHeight, calculateTextMultilineHeight(step.name, settings.paintTextBottom));
        }

        calculatedHieght += bottomTextHeight;

        calculatedHieght += 10; // padding bottom
    }

    public void setTextFormatter(TextFormatter textFormatter) {
        this.textFormatter = textFormatter;
        update();
    }

    public void setRegionTextFormatter(RegionTextFormatter regionTextFormatter) {
        this.regionTextFormatter = regionTextFormatter;
        update();
    }

    private EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner());

    public void postInvalidate() {
        eventHandler.postTask(this::invalidate);
    }

    private float dpToPx(int size) {
        return AttrHelper.fp2px(size, getContext());
    }

    private float dpToPx(float size) {
        return AttrHelper.fp2px(size, getContext());
    }

    public interface EditListener {
        void onEditStarted(TextField editText);
    }

    public interface Listener {
        void valueChanged(Slidr slidr, float currentValue);
    }

    public interface BubbleClickedListener {
        void bubbleClicked(Slidr slidr);
    }

    public interface TextFormatter {
        String format(float value);
    }

    public interface RegionTextFormatter {
        String format(int region, float value);
    }

    private class Bubble {
        private float height;
        private float width;
        private float x;
        private float y;

        public boolean clicked(TouchEvent e) {
            return e.getPointerPosition(0).getX() >= x
                    && e.getPointerPosition(0).getX() <= x + width
                    && e.getPointerPosition(0).getY() >= y
                    && e.getPointerPosition(0).getY() < y + height;
        }

        public float getHeight() {
            return height - dpToPx(BUBBLE_ARROW_HEIGHT);
        }

        public float getX() {
            return Math.max(x, 0);
        }

        public float getY() {
            return Math.max(y, 0);
        }
    }

    public class EurosTextFormatter implements TextFormatter {
        @Override
        public String format(float value) {
            return String.format("%d €", (int) value);
        }
    }

    public static class Step implements Comparable<Step> {
        private String name;
        private float value;

        private float xStart;
        private Color colorBefore;
        private Color colorAfter = new Color(0xFFED5564);

        public Step(String name, float value, Color colorBefore) {
            this.name = name;
            this.value = value;
            this.colorBefore = colorBefore;
        }

        public Step(String name, float value, Color colorBefore, Color colorAfter) {
            this(name, value, colorBefore);
            this.colorAfter = colorAfter;
        }

        @Override
        public int compareTo(Step o) {
            return Float.compare(value, o.value);
        }
    }

    public class Settings {
        private Slidr slidr;
        private Paint paintBar;
        private Paint paintIndicator;
        private Paint paintStep;
        private Paint paintTextTop;
        private Paint paintTextBottom;
        private Paint paintBubbleTextCurrent;
        private Paint paintBubble;
        private Color colorBackground = Color.GRAY;
        private Color colorStoppover = Color.BLACK;
        private Color textColor = new Color(0xFF6E6E6E);
        private int textTopSize = (int) dpToPx(12);
        private int textBottomSize = (int) dpToPx(12);
        private int textSizeBubbleCurrent = 16;
        private float barHeight = dpToPx(15);
        private float paddingCorners = 0;
        private boolean step_colorizeAfterLast = false;
        private boolean step_drawLines = true;
        private boolean step_colorizeOnlyBeforeIndicator = true;
        /**
         * 上方绘制
         */
        private boolean drawTextOnTop = true;
        /**
         * 下方绘制
         */
        private boolean drawTextOnBottom = true;
        /**
         * 气泡绘制
         */
        private boolean drawBubble = true;
        /**
         * 分区
         */
        private boolean modeRegion = false;
        /**
         * 指示器显示位置在进度条里面还是外面，默认外面
         */
        private boolean indicatorInside = false;
        /**
         * 字跟随
         */
        private boolean regions_textFollowRegionColor = false;
        /**
         * 中间字
         */
        private boolean regions_centerText = true;
        /**
         * 左边颜色
         */
        private Color regionColorLeft = new Color(0xFF007E90);
        /**
         * 右边颜色
         */
        private Color regionColorRight = new Color(0xFFED5564);
        /**
         * 气泡点击
         */
        private boolean editOnBubbleClick = true;
        /**
         * 气泡颜色
         */
        private Color bubbleColorEditing = Color.WHITE;

        Settings(Slidr slidr) {
            this.slidr = slidr;

            paintIndicator = new Paint();
            paintIndicator.setAntiAlias(true);
            paintIndicator.setStrokeWidth(2);

            paintBar = new Paint();
            paintBar.setAntiAlias(true);
            paintBar.setStrokeWidth(2);
            paintBar.setColor(colorBackground);

            paintStep = new Paint();
            paintStep.setAntiAlias(true);
            paintStep.setStrokeWidth(5);
            paintStep.setColor(colorStoppover);

            paintTextTop = new Paint();
            paintTextTop.setAntiAlias(true);
            paintTextTop.setStyle(Paint.Style.FILL_STYLE);
            paintTextTop.setColor(textColor);
            paintTextTop.setTextSize(textTopSize);

            paintTextBottom = new Paint();
            paintTextBottom.setAntiAlias(true);
            paintTextBottom.setStyle(Paint.Style.FILL_STYLE);
            paintTextBottom.setColor(textColor);
            paintTextBottom.setTextSize(textBottomSize);

            paintBubbleTextCurrent = new Paint();
            paintBubbleTextCurrent.setAntiAlias(true);
            paintBubbleTextCurrent.setStyle(Paint.Style.FILL_STYLE);
            paintBubbleTextCurrent.setColor(Color.WHITE);
            paintBubbleTextCurrent.setStrokeWidth(2);
            paintBubbleTextCurrent.setTextSize(AttrHelper.fp2px(textSizeBubbleCurrent, slidr.getContext()));

            paintBubble = new Paint();
            paintBubble.setAntiAlias(true);
            paintBubble.setStrokeWidth(3);
        }

        public void init(Context context, AttrSet attrs) {
            if (attrs != null) {
                attrs.getAttr("slidr_backgroundColor").ifPresent(attr -> setColorBackground(attr.getColorValue()));

                attrs.getAttr("slidr_step_colorizeAfterLast")
                        .ifPresent(attr -> this.step_colorizeAfterLast = attr.getBoolValue());
                attrs.getAttr("slidr_step_drawLine").ifPresent(attr -> this.step_drawLines = attr.getBoolValue());
                attrs.getAttr("slidr_step_colorizeOnlyBeforeIndicator")
                        .ifPresent(attr -> this.step_colorizeOnlyBeforeIndicator = attr.getBoolValue());

                attrs.getAttr("slidr_textTop_visible").ifPresent(attr -> this.drawTextOnTop = attr.getBoolValue());
                attrs.getAttr("slidr_textTop_size")
                        .ifPresent(attr -> setTextTopSize(AttrHelper.fp2px(attr.getFloatValue(), context)));
                attrs.getAttr("slidr_textBottom_visible")
                        .ifPresent(attr -> this.drawTextOnBottom = attr.getBoolValue());
                attrs.getAttr("slidr_textBottom_size")
                        .ifPresent(attr -> setTextBottomSize(AttrHelper.fp2px(attr.getFloatValue(), context)));

                attrs.getAttr("slidr_barHeight").ifPresent(attr -> this.barHeight = attr.getDimensionValue());
                attrs.getAttr("slidr_draw_bubble").ifPresent(attr -> this.drawBubble = attr.getBoolValue());
                attrs.getAttr("slidr_regions").ifPresent(attr -> this.modeRegion = attr.getBoolValue());

                attrs.getAttr("slidr_region_leftColor").ifPresent(attr -> this.regionColorLeft = attr.getColorValue());
                attrs.getAttr("slidr_region_rightColor")
                        .ifPresent(attr -> this.regionColorRight = attr.getColorValue());

                attrs.getAttr("slidr_indicator_inside").ifPresent(attr -> this.indicatorInside = attr.getBoolValue());
                attrs.getAttr("slidr_regions_textFollowRegionColor")
                        .ifPresent(attr -> this.regions_textFollowRegionColor = attr.getBoolValue());
                attrs.getAttr("slidr_regions_centerText")
                        .ifPresent(attr -> this.regions_centerText = attr.getBoolValue());

                attrs.getAttr("slidr_edditable").ifPresent(attr -> this.editOnBubbleClick = attr.getBoolValue());
            }
        }

        public void setStep_colorizeAfterLast(boolean step_colorizeAfterLast) {
            this.step_colorizeAfterLast = step_colorizeAfterLast;
            slidr.update();
        }

        public void setDrawTextOnTop(boolean drawTextOnTop) {
            this.drawTextOnTop = drawTextOnTop;
            slidr.update();
        }

        public void setDrawTextOnBottom(boolean drawTextOnBottom) {
            this.drawTextOnBottom = drawTextOnBottom;
            slidr.update();
        }

        public void setDrawBubble(boolean drawBubble) {
            this.drawBubble = drawBubble;
            slidr.update();
        }

        public void setModeRegion(boolean modeRegion) {
            this.modeRegion = modeRegion;
            slidr.update();
        }

        public void setRegionColorLeft(Color regionColorLeft) {
            this.regionColorLeft = regionColorLeft;
            slidr.update();
        }

        public void setRegionColorRight(Color regionColorRight) {
            this.regionColorRight = regionColorRight;
            slidr.update();
        }

        public void setColorBackground(Color colorBackground) {
            this.colorBackground = colorBackground;
            slidr.update();
        }

        public void setTextTopSize(int textSize) {
            this.textTopSize = textSize;
            this.paintTextTop.setTextSize(textSize);
            slidr.update();
        }

        public void setTextBottomSize(int textSize) {
            this.textBottomSize = textSize;
            this.paintTextBottom.setTextSize(textSize);
            slidr.update();
        }
    }
}
